package main

import (
	"flag"
	"fmt"
	"io"
	"os"
	"path/filepath"

	"gitee.com/zhi/binexpr"
)

type Console struct{}

func NewConsole() *Console {
	fmt.Println("# Welcome to expression interpreter!")
	fmt.Println("# You can input any supported expressions and press enter to execute.")
	fmt.Println("# Input 'Ctrl+D' to exit.")
	fmt.Println()
	return &Console{}
}

func (c *Console) ReadLine() ([]byte, error) {
	var result []byte
	buf := make([]byte, 1)
	fmt.Print("> ")
	for {
		read, err := os.Stdin.Read(buf)
		if err != nil {
			return nil, err
		}
		if read <= 0 {
			return result, nil
		}
		switch buf[0] {
		case 0, 4:
			return nil, io.EOF
		case byte('\r'):
			continue
		case byte('\n'):
			return result, nil
		default:
			result = append(result, buf...)
		}
	}
}

func repl(interpreter *binexpr.Interpreter) error {
	console := NewConsole()
	for {
		line, err := console.ReadLine()
		if err != nil {
			if err == io.EOF {
				return nil
			}
			return err
		}
		val, err := interpreter.Evaluate(string(line), nil)
		if err != nil {
			return err
		}
		fmt.Printf("%v\n", val)
	}
}

func run() error {
	var code string

	flag.StringVar(&code, "c", "", "the code to run")
	flag.Usage = func() {
		fmt.Printf("Usage: %s [expr]\n", filepath.Base(os.Args[0]))
		flag.PrintDefaults()
	}
	flag.Parse()

	interpreter := binexpr.NewInterpreter()

	if flag.NArg() == 0 && flag.NArg() == 0 {
		if err := repl(interpreter); err != nil {
			return err
		}
		return nil
	}

	if code == "" && flag.NArg() > 1 {
		data, err := os.ReadFile(flag.Arg(0))
		if err != nil {
			return err
		}
		code = string(data)
	}

	ret, err := interpreter.Evaluate(code, nil)
	if err != nil {
		return err
	}
	fmt.Println(ret)
	return nil
}

func main() {
	err := run()
	if err != nil {
		panic(err)
	}
}
